﻿/*
 * @Autor: PK
 * @Date: 2021/12/07 21:12:SS
 */

using System.Collections.Generic;
using System.Linq;
using NLog;
using O2DESNet;
using ShkSimulation.core.dao.output.result;
using ShkSimulation.core.dao.statistic;
using ShkSimulation.core.exceptions;

namespace ShkSimulation.core.template {

	public class NetworkResultHolder : NetworkResultCollector {
		
		protected static readonly Logger logger = LogManager.GetLogger(nameof(NetworkResultCollector));

		public NetworkResultHolder() {
			DataReporterGenerator = new DataReporterGenerator();
		}
		
		public Dictionary<string, List<DataReporter>> AllDataReporters { get; set; }

		protected Dictionary<string, Result> results = new Dictionary<string, Result>();

		public IReadOnlyDictionary<string, Result> AllOutputData {
			get {
				return results.AsReadOnly();
			}
		}
		
		public DataReporterGenerator DataReporterGenerator { get; set; }
		
		public Dictionary<DataReporterCollection, ResultListener> ReporterAndListener { get; private set; }
		
		public Dictionary<string, DataReporterCollection> ReporterCollections { get; private set; }

		public Result GetCurrentResult(string key) {
			if (ReporterAndListener == null || ReporterAndListener.Count == 0) {
				return new DefaultDoubleResult("empty result!");
			}
			foreach (var (dataReporterCollection, resultListener) in ReporterAndListener) {
				resultListener.OnReporterReport(dataReporterCollection);
			}
			ResultData data = new ResultData();
			foreach (var resultListener in ReporterAndListener.Values) {
				data = data + resultListener.Export();
			}
			return new DefaultDoubleResult(key, data);
		}

		public NetworkResultCollector Clone() {
			NetworkResultHolder holder = new NetworkResultHolder();
			holder.DataReporterGenerator = DataReporterGenerator.Clone();
			return holder;
		}

		public void InitStatistics(Network network) {
			RemoveAllStatistic();
			AllDataReporters = DataReporterGenerator.CreateReportersByNetwork(network);
			ReporterCollections = PackRepoterToCollection(AllDataReporters);
			ReporterAndListener = ListenerGenerator.CreateListenersByReporters(ReporterCollections);
			RegisterStatisticWithoutAdd(ReporterAndListener);
		}
				
		private Dictionary<string, DataReporterCollection> PackRepoterToCollection(Dictionary<string, List<DataReporter>> reporters) {
			Dictionary<string, DataReporterCollection> dataReporters = new Dictionary<string, DataReporterCollection>();
			foreach (var (key, reporterList) in reporters) {
				if (reporterList.Count == 0) {
					logger.Info("Reporter列表为空，不创建汇报集合: {Key}", key);
					continue;
				}
				DataReporterCollection collection = DataReporterCollection.CreateByImplements(key, reporterList);
				dataReporters.Add(key, collection);
			}
			return dataReporters;
		}

		public void ReplaceAndRegisterStatistic(Dictionary<DataReporterCollection, ResultListener> reporterToListeners) {
			RegisterStatisticWithoutAdd(reporterToListeners);
			ReporterAndListener = reporterToListeners;
			ReporterCollections = reporterToListeners.ToDictionary(each => each.Key.Uniquekey, each => each.Key);
		}

		private void RegisterStatisticWithoutAdd(Dictionary<DataReporterCollection, ResultListener> reporterToListeners) {
			ISet<string> reporterSet = new HashSet<string>();
			foreach ((var reporterCollection, var resultListener) in reporterToListeners) {
				if (reporterSet.Contains(resultListener.Uniquekey)) {
					throw new SimulationInitException("同一DataReporter不允许有相同UniqueKey的ResultListener");
				}
				reporterSet.Add(resultListener.Uniquekey);
				logger.Info($"Register Listener {resultListener.Uniquekey} to {reporterCollection.Uniquekey}");
				resultListener.BindTargetReporter(reporterCollection);
			}
		}
				
		public void RegisterStatistic(DataReporterCollection collection, ResultListener reporterToListener) {
			if (ReporterAndListener.ContainsKey(collection)) {
				throw new SimulationInitException($"不能重复添加DataReporterCollection：{collection.Uniquekey}");
			}
			logger.Info($"Register Listener {reporterToListener.Uniquekey} to {collection.Uniquekey}");
			reporterToListener.BindTargetReporter(collection);
			ReporterAndListener.Add(collection, reporterToListener);
			if (!ReporterCollections.ContainsKey(collection.Uniquekey)) {
				ReporterCollections.Add(collection.Uniquekey, collection);
			}
		}
		
		public void RemoveAllStatistic() {
			var reporterToListeners = ReporterAndListener;
			if (ReporterAndListener == null) {
				return;
			}
			foreach (var (dataReporterCollection, resultListener) in reporterToListeners) {
				logger.Info($"Remove Listener {resultListener.Uniquekey} from {dataReporterCollection.Uniquekey}");
				resultListener.RemoveTargetReporter(dataReporterCollection);
			}
			this.ReporterCollections = new Dictionary<string, DataReporterCollection>();
			this.ReporterAndListener = new Dictionary<DataReporterCollection, ResultListener>();
		}
		
		public void RemoveStatistic(string reporterUniqueKey) {
			var dataReporterCollection = ReporterCollections[reporterUniqueKey];
			if (dataReporterCollection == null) {
				return;
			}
			var listener = ReporterAndListener[dataReporterCollection];
			if (listener == null) {
				return;
			}
			logger.Info($"Remove Listener {listener.Uniquekey} from {dataReporterCollection.Uniquekey}");
			ReporterCollections.Remove(reporterUniqueKey);
			ReporterAndListener.Remove(dataReporterCollection);
			listener.RemoveTargetReporter(dataReporterCollection);
		}

	}
}